package com.liruo.enumconverter.annotation;

import com.liruo.enumconverter.model.SourceEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Slf4j
public class EnumConverterRegistrar implements ImportBeanDefinitionRegistrar{
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
        String[] packages = getPackages(importingClassMetadata);
        if (packages == null) return;

        Set<Class<? extends SourceEnum<?>>> autoEnumClassSet = new HashSet<>();
        for (String pkg : packages) {
            if (StringUtils.hasText(pkg)) {
                autoEnumClassSet.addAll(convertToClass(pkg));
            }
        }

        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(SourceEnums.class);
        definition.addPropertyValue("sourceEnumSet", autoEnumClassSet);
        registry.registerBeanDefinition("sourceEnums", definition.getBeanDefinition());
    }

    private String[] getPackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata
                .getAnnotationAttributes(EnableSourceEnumConverter.class.getName());
        if(attributes == null){
            if(log.isDebugEnabled()){
                log.debug("{} has not use {} on {}",
                        EnumConverterRegistrar.class.getName()
                        , EnableSourceEnumConverter.class.getName()
                        , importingClassMetadata.getClassName());
            }
            return null;
        }

        if(log.isDebugEnabled()){
            log.debug(EnumConverterRegistrar.class.getName() + " is collecting SourceEnums");
        }

        String[] packages = (String[])attributes.get("value");
        if(packages == null || packages.length == 0){
            packages = (String[])attributes.get("packages");
            if(packages == null || packages.length == 0){
                if(log.isDebugEnabled()){
                    log.debug("the annotation {}  on {} has not value and packages, so use {} package",
                            EnableSourceEnumConverter.class.getName(),
                            importingClassMetadata.getClassName(),
                            importingClassMetadata.getClassName());
                }
                packages = new String[]{ClassUtils.getPackageName(importingClassMetadata.getClassName())};
            }
        }
        return packages;
    }

    private Set<Class<? extends SourceEnum<?>>> convertToClass(String packagePath) {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Set<Class<? extends SourceEnum<?>>> autoEnumClasses = new HashSet<>();
        packagePath = packagePath.replaceAll("\\.", "/");
        packagePath = "classpath*:" + packagePath + "/**/*.class";
        try {
            Resource[] resources = resolver.getResources(packagePath);
            for (Resource res : resources) {
                // 先获取resource的元信息，然后获取class元信息，最后得到 class 全路径M
                String clsName = new SimpleMetadataReaderFactory()
                        .getMetadataReader(res)
                        .getClassMetadata()
                        .getClassName();
                // 通过名称加载
                Class<?> clazz = Class.forName(clsName);
                if (SourceEnum.class.isAssignableFrom(clazz) && clazz.isEnum()) {
                    autoEnumClasses.add((Class<? extends SourceEnum<?>>)clazz);
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new IllegalArgumentException("parse packagePath error : {}" + e);
        }
        return autoEnumClasses;
    }
}